package com.qj.tool;

import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileInputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;

/**
 * @author dlw
 * @date 2025-06-04 10:20
 * @desc 我们希望如果我们使用动态配置，比如从Nacos 或者 SpringConfig 中来获取配置文件，
 * 当配置中心进行了改变，需要我们这边立即生效！
 */
@Slf4j
public class PropertiesUtils {
    /**
     * 属性文件与属性集合的关系
     */
    private Map<String, Properties> propertiesMap = new HashMap<>();
    /**
     * 属性文件与修改时间的关系
     */
    private Map<String, Long> modifiedTimeMap = new HashMap<>();

    /**
     * 指定的属性文件的目录路径
     */
    private String configPath = "";

    /**
     * 设置文件的目录路径
     */
    public void setConfigPath(String path) {
        this.configPath = path;
    }

    /**
     * 单例模式
     */
    private PropertiesUtils() {

    }

    private static class SingleHolder {
        private static PropertiesUtils instance = new PropertiesUtils();
    }

    public static PropertiesUtils getInstance() {
        return SingleHolder.instance;
    }

    /**
     * 获取指定属性的值
     */
    public String getPropertyValue(String propertyFileName, String key) {
        String fileName = convertPropertiesFileName(propertyFileName);
        try {
            if (propertiesMap.get(fileName) == null) {
                // 如果该配置文件未加载，那么加载指定名称的属性文件
                loadProperties(fileName);
            } else {
                // 如果该配置文件已经加载，检查属性文件有无更新，若更新则重新加载
                checkPropertiesFileModified(fileName);
            }
            return propertiesMap.get(fileName).getProperty(key);
        } catch (Exception ex) {
            log.error("PropertiesUtils.getPropertyValue.error:{}", ex.getMessage(), ex);
            return "";
        }
    }

    /**
     * 对传入的属性文件名称进行处理，如果包含.properties后缀则去掉
     */
    private String convertPropertiesFileName(String propertyFileName) {
        String fileName = propertyFileName;
        if (fileName.endsWith(".properties")) {
            int index = fileName.lastIndexOf('.');
            fileName = fileName.substring(0, index);
        }
        return fileName;
    }

    /**
     * 加载指定名称的属性文件
     */
    private void loadProperties(String shortPropertyFileName) throws URISyntaxException {
        // 根据配置文件的名称，来获取相应配置文件
        File file = getPropertiesFile(shortPropertyFileName);
        // 获取该文件最新修改时间
        Long modifiedTime = file.lastModified();

        if (propertiesMap.get(shortPropertyFileName) != null) {
            propertiesMap.remove(shortPropertyFileName);
        }

        Properties properties = new Properties();
        try {
            properties.load(new FileInputStream(file));
        } catch (Exception ex) {
            log.error("PropertiesUtils.loadProperties.error:{}", ex.getMessage(), ex);
        }
        propertiesMap.put(shortPropertyFileName, properties);
        modifiedTimeMap.put(shortPropertyFileName, modifiedTime);
    }

    /**
     * 获取属性文件的路径
     */
    private File getPropertiesFile(String shortPropertyFileName) throws URISyntaxException {
        File propertiesFile = null;
        // 1) 如果配置文件路径不为空，则进行拼接
        if (this.configPath != null && !this.configPath.trim().equals("")) {
            return new File(this.configPath + File.separator + shortPropertyFileName + ".properties");
        }
        // 2) 如果为空，那么就使用内置的user.dir路径
        String dir = System.getProperty("user.dir") + File.separator + shortPropertyFileName + ".properties";
        // 3) 如果内置路径没有文件，那么就从最终指定路径中查找
        propertiesFile = new File(dir);
        //如果user.dir不存在
        if (!propertiesFile.exists()) {
            // 同级（编译路径）的resource根目录下，是否存在对应文件，存在则返回该地址
            URL url = PropertiesUtils.class.getResource("/" + shortPropertyFileName + ".properties");
            if (url == null) {
                propertiesFile = null;
            } else {
                propertiesFile = new File(url.toURI());
            }
        }
        return propertiesFile;
    }

    /**
     * 检查属性文件有无更新，若更新则重新加载
     */
    private void checkPropertiesFileModified(String shortPropertyFileName) throws URISyntaxException {
        File file = getPropertiesFile(shortPropertyFileName);
        Long modifiedTime = file.lastModified();
        Long lastModifiedTime = modifiedTimeMap.get(shortPropertyFileName);
        if (modifiedTime == 0 && lastModifiedTime == null) {
            log.error(shortPropertyFileName + ".properties file does not exist!");
        } else if (modifiedTime > lastModifiedTime) {
            loadProperties(shortPropertyFileName);
        }
    }
}
